package cn.jiangjiesheng.bootstrap.commons.utils;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by dev@jiangjiesheng.cn on 2021/10/11  <br/>
 *
 * @author dev@jiangjiesheng.cn
 */
public class BeanCopyUtils {

    public static <T> T copyBean(Object s, Class<T> clazz) {
        T obj = null;
        try {
            obj = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        BeanUtils.copyProperties(s, obj);
        return clazz.cast(obj);
    }

    public static <T> List<T> copyBeanList(List source, Class<T> clazz) {
        List<T> returning = new ArrayList<>();
        try {
            for (Object s : source) {
                T o = null;
                o = clazz.newInstance();
                BeanUtils.copyProperties(s, o);
                returning.add(o);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return returning;
    }
    public static <T> boolean haveSamePropertyValues(Class<T> type, T t1, T t2) throws Exception {

        BeanInfo beanInfo = Introspector.getBeanInfo(type);
        for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
            Method m = pd.getReadMethod();
            Object o1 = m.invoke(t1);
            Object o2 = m.invoke(t2);
            if (!Objects.equals(o1, o2)) {
                return false;
            }
        }
        return true;
    }

    public static <T> String diffPropertyValues(Class<T> type, T t1, T t2) throws Exception {
        StringBuffer sb = new StringBuffer();
        BeanInfo beanInfo = Introspector.getBeanInfo(type);
        for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
            Method m = pd.getReadMethod();
            Object o1 = m.invoke(t1); // 获取value
            Object o2 = m.invoke(t2); // 获取value
            if (!Objects.equals(o1, o2)) {
                sb.append(m.getName()).append(o1).append("->").append(o2);
            }
        }
        return sb.toString();
    }

    public static <T> T copyNonNullProperties(T target, T in) {
        if (in == null || target == null || target.getClass() != in.getClass()) return null;

        final BeanWrapper src = new BeanWrapperImpl(in);
        final BeanWrapper trg = new BeanWrapperImpl(target);

        for (final Field property : target.getClass().getDeclaredFields()) {
            Object providedObject = src.getPropertyValue(property.getName());
            if (providedObject != null && !(providedObject instanceof Collection<?>)) {
                trg.setPropertyValue(
                        property.getName(),
                        providedObject);
            }
        }
        return target;
    }

    public static <T> List<T> mapsToClazz(List<Map<String, Object>> maps, Class<T> clazz) {
        List<T> returning = new ArrayList<>();
        try {
            for (Map<String, Object> map : maps) {
                T o = clazz.newInstance();
                org.apache.commons.beanutils.BeanUtils.populate(o, map);
                returning.add(o);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return returning;
    }

    public static <T> T mapToClazz(Map<String, Object> map, Class<T> clazz) {
        if (Objects.isNull(map) && map.isEmpty()) {
            return null;
        }
        T o = null;
        try {
            o = clazz.newInstance();
            org.apache.commons.beanutils.BeanUtils.populate(o,map);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return o;
    }

}
